<?php

namespace App\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

class LoginAttemptService
{
    protected $maxAttempts;
    protected $lockoutTime;
    protected $prefix;

    public function __construct()
    {
        $this->maxAttempts = config('auth.max_login_attempts', 5);
        $this->lockoutTime = config('auth.lockout_time', 900); // 15分钟
        $this->prefix = 'login_attempts:';
    }

    /**
     * 记录登录失败尝试
     *
     * @param string $identifier 用户标识（用户名或IP）
     * @return void
     */
    public function recordFailedAttempt(string $identifier): void
    {
        $key = $this->getKey($identifier);
        $attempts = Cache::get($key, 0) + 1;
        
        Cache::put($key, $attempts, $this->lockoutTime);
        
        if ($attempts >= $this->maxAttempts) {
            $lockoutKey = $this->getLockoutKey($identifier);
            Cache::put($lockoutKey, true, $this->lockoutTime);
            Log::warning("登录失败次数过多，账户已锁定: {$identifier}");
        }
    }

    /**
     * 清除登录失败尝试
     *
     * @param string $identifier
     * @return void
     */
    public function clearAttempts(string $identifier): void
    {
        $key = $this->getKey($identifier);
        $lockoutKey = $this->getLockoutKey($identifier);
        
        Cache::forget($key);
        Cache::forget($lockoutKey);
    }

    /**
     * 检查是否被锁定
     *
     * @param string $identifier
     * @return bool
     */
    public function isLocked(string $identifier): bool
    {
        $lockoutKey = $this->getLockoutKey($identifier);
        return Cache::has($lockoutKey);
    }

    /**
     * 获取剩余尝试次数
     *
     * @param string $identifier
     * @return int
     */
    public function remainingAttempts(string $identifier): int
    {
        $key = $this->getKey($identifier);
        $attempts = Cache::get($key, 0);
        return max(0, $this->maxAttempts - $attempts);
    }

    /**
     * 获取锁定剩余时间（秒）
     *
     * @param string $identifier
     * @return int
     */
    public function getLockoutRemainingTime(string $identifier): int
    {
        try {
            $lockoutKey = $this->getLockoutKey($identifier);
            
            // 检查锁定键是否存在
            if (!Cache::has($lockoutKey)) {
                return 0;
            }
            
            // 尝试使用Redis获取精确的TTL
            try {
                $store = Cache::getStore();
                
                // 如果使用Redis缓存，尝试获取精确的TTL
                if (method_exists($store, 'connection') && $store instanceof \Illuminate\Cache\RedisStore) {
                    // 获取缓存前缀
                    $prefix = method_exists($store, 'getPrefix') ? $store->getPrefix() : Cache::getPrefix();
                    $fullKey = $prefix . $lockoutKey;
                    
                    // 使用Redis facade获取TTL
                    $redis = \Illuminate\Support\Facades\Redis::connection('cache');
                    $ttl = $redis->ttl($fullKey);
                    
                    if ($ttl > 0) {
                        return $ttl;
                    } elseif ($ttl === -1) {
                        // 键存在但没有设置过期时间，返回锁定时间
                        return $this->lockoutTime;
                    }
                    // $ttl === -2 表示键不存在
                    return 0;
                }
            } catch (\Exception $e) {
                // 如果获取TTL失败，记录调试信息但不影响功能
                \Log::debug('无法获取精确TTL，使用默认锁定时间: ' . $e->getMessage());
            }
            
            // 如果键存在但无法获取精确TTL，返回锁定时间
            // 这样可以确保即使缓存驱动不是Redis也能正常工作
            return $this->lockoutTime;
            
        } catch (\Exception $e) {
            \Log::warning('获取锁定剩余时间异常: ' . $e->getMessage());
            // 发生异常时，尝试简单检查键是否存在
            try {
                $lockoutKey = $this->getLockoutKey($identifier);
                if (Cache::has($lockoutKey)) {
                    return $this->lockoutTime;
                }
            } catch (\Exception $ex) {
                // 忽略二次异常
            }
            return 0;
        }
    }

    protected function getKey(string $identifier): string
    {
        return $this->prefix . md5($identifier);
    }

    protected function getLockoutKey(string $identifier): string
    {
        return $this->prefix . 'lockout:' . md5($identifier);
    }
}

